/*******************************************************************************
 * Copyright (C) 2010 Devin Samarin.
 * 
 * This file is part of the SUIT Toolkit.
 * 
 * The SUIT Toolkit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * The SUIT Toolkit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with the SUIT Toolkit.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package suit.ui;

import java.util.Timer;
import java.util.TimerTask;

import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.GameCanvas;

/**
 * Think of this as the Window of the Application. One single Widget can be
 * added to this at a time. Events are passed from here using the native Canvas
 * events to the Application. See the documentation for
 * javax.microedition.lcdui.GameCanvas for more information.
 * 
 * @author Devin Samarin
 * @see javax.microedition.lcdui.game.GameCanvas
 */
public class Screen extends GameCanvas implements Runnable {

	private int pointX, pointY;

	private int sleep;
	protected boolean update = true;
	public boolean realized = false;

	private Widget child;
	private boolean hasChild;

	private Timer longPressTimer;
	private int longPressSeconds = 2;
	private int longPressThreshold = 10;

	private Rectangle clip = new Rectangle(0,0,0,0);

	protected Application app;
	protected Display display;
	protected Theme theme;

	// For focusing Widgets with keys
	/*private Widget current;


	public void moveNextFocus() {

	}

	public void movePreviousFocus() {

	}*/

	/**
	 * Constructs a new Screen object with the frame rate set to 0.
	 * 
	 * <pre>
	 * Screen screen = new Screen(new DefaultTheme());
	 * </pre>
	 * 
	 * @param theme
	 *            the theme to use for the widgets
	 */
	public Screen(Theme theme) {
		this(theme, 12);
	}

	/**
	 * Constructs a new Screen object with the theme and framerate
	 * 
	 * @param theme
	 *            the theme to use for the widgets
	 * @param framerate
	 *            the speed to redraw
	 */
	public Screen(Theme theme, int framerate) {
		super(true);
		this.theme = theme;
		setFrameRate(framerate);
		new Thread(this).start();
	}

	/**
	 * Number of times to update in a second. Set to 0 update as soon as a
	 * change occurs in the screen
	 * 
	 * @param framerate
	 *            the number of times to update in a second
	 */
	public void setFrameRate(int framerate) {
		if (framerate == 0) {
			sleep = 0;
		} else {
			sleep = 1000 / framerate;
		}
	}
	

	public int getFrameRate() {
		return 1000 / sleep;
	}


	public void run() {
		while (true) {
			if (update && realized) {
				update = false;
				flushGraphics(clip.x, clip.y, clip.w, clip.h);
			}
			if (sleep > 0) {
				try {
					Thread.sleep(sleep);
				} catch (InterruptedException e) {
				}
			}
		}
	}

	/**
	 * Redraws the child Widget.
	 * 
	 * Use this method in order to update the screen. The child Widget is
	 * redrawn and it bubbles down into each of that Widget's children.
	 */
	public void invalidate() {
		invalidate(getGraphics());
		update = true;
		clip = new Rectangle(0, 0, getWidth(), getHeight());
	}
	
	public void invalidate(Widget w) {
		invalidate(getGraphics());
		update = true;
		clip = w.allocation;
	}
	
	private void invalidate(Graphics g) {
		int x, y, w, h;
		x = 0;
		y = 0;
		w = getWidth();
		h = getHeight();
		g.setColor(0x0);
		g.fillRect(0, 0, w, h);
		if (hasChild) {
			child.sizeAllocate(new Rectangle(x, y, w, h));
			child.registerEvent(new Event(Event.Expose, g, theme));
		}
	}

	/**
	 * Returns an image with the width and height of the Screen and the contents
	 * of the Screen's child
	 * 
	 * @return an image with the width and height of the Screen and the contents
	 *         of the Screen's child
	 */
	public Image getScreenshot() {
		Image image = Image.createImage(getWidth(), getHeight());
		invalidate(image.getGraphics());
		return image;
	}

	protected void pointerPressed(int x, int y) {
		if (app.onPointerPressed(x, y)) {
			pointX = x;
			pointY = y;
			longPressTimer = new Timer();
			longPressTimer.schedule(new TimerTask() {
				public void run() {
					child.registerEvent(new Event(Event.LongPress, pointX,
							pointY));
				}
			}, longPressSeconds * 1000);
			child.registerEvent(new Event(Event.PointerPressed, x, y));
		}
	}

	protected void pointerDragged(int x, int y) {
		if (app.onPointerDragged(x, y)) {
			if (Math.abs(pointX - x) > longPressThreshold
					|| Math.abs(pointY - y) > longPressThreshold) {
				longPressTimer.cancel();
			}
			child.registerEvent(new Event(Event.PointerDragged, x, y));
		}
	}

	protected void pointerReleased(int x, int y) {
		if (app.onPointerReleased(x, y)) {
			longPressTimer.cancel();
			child.registerEvent(new Event(Event.PointerReleased, x, y));
		}
	}

	protected void keyPressed(int key) {
		if (app.onKeyPressed(key)) {
			child.registerEvent(new Event(Event.KeyPressed, key));
		}
	}

	protected void keyRepeated(int key) {
		if (app.onKeyRepeated(key)) {
			child.registerEvent(new Event(Event.KeyRepeated, key));
		}
	}

	protected void keyReleased(int key) {
		if (app.onKeyReleased(key)) {
			child.registerEvent(new Event(Event.KeyReleased, key));
		}
	}

	/**
	 * Sets the Screen to be full screen, and then redraws the child.
	 * 
	 * @see javax.microedition.lcdui.Canvas#setFullScreenMode(boolean)
	 */
	public void setFullScreenMode(boolean fullscreen) {
		super.setFullScreenMode(fullscreen);
		invalidate();
	}

	/**
	 * Adds a Widget to the Screen and draws the Widget
	 * 
	 * @param widget
	 *            the widget to add to the screen
	 */
	public void add(Widget widget) {
		child = widget;
		hasChild = true;
		widget.isRoot = true;
		widget.setScreen(this);
		invalidate();
	}
}